#include "decryptor.h"

Decryptor::Decryptor(Context *params, SecretKey *sk) : params(params), sk(sk)
{
    Ring *DecRingQ;
    if ((DecRingQ = new Ring(params->N(), params->qi)) == nullptr) {
        throw std::runtime_error("Failed to create Ring");
    }
    this->ringQ = DecRingQ;
    this->polyPool = DecRingQ->NewPoly();
}

Plaintext *Decryptor::DecryptNew(Ciphertext *ciphertext) const
{
    auto *p = new Plaintext(this->params, params->QCount() - 1);
    this->Decrypt(ciphertext, p);
    return p;
}
/*
 * @brief 将密文进行解密，解密的明文存储在p指针对应的空间中
 *
 * @param ciphertext 需要解密的密文，用指针做参数，已开辟空间
 * @param p 解密的明文，用指针做参数，已开辟空间
 */
void Decryptor::Decrypt(Ciphertext *ciphertext, Plaintext *p) const
{
    Ring *DecRingQ = this->ringQ;
    Poly *tmp = this->polyPool;
    DecRingQ->NTT(ciphertext->value[ciphertext->Degree()], p->value);
    for (uint64_t i = ciphertext->Degree(); i > 0; i--) {
        DecRingQ->MulCoeffsBarrett(p->value, this->sk->value.Q, p->value);
        DecRingQ->NTT(ciphertext->value[i - 1], tmp);
        DecRingQ->Add(p->value, tmp, p->value);
        if ((i & 3) == 3) {
            DecRingQ->Reduce(p->value, p->value);
        }
    }
    if ((ciphertext->Degree() & 3) != 3) {
        DecRingQ->Reduce(p->value, p->value);
    }
    DecRingQ->InvNTT(p->value, p->value);
}
